# -*- coding: utf-8 -*-
#############################
####### server module #######
#############################
########## INFO #############
#
# program: chat
#    file: server.py
#  python: version 2.6 is required
#     use: python -i server.py
#   autor: Alexander Anikeev
# version: 0.9.9 RC2
#  update: 26.03.11
#

# import modules
from socket import * 
from string import *
from sys import *
from threading import * 
from select import * 
from time import *
import socket, string, sys, threading, select, time
from zlib import crc32
from random import randint
from cPickle import dumps
# default globals
global HOST, PORT, LOGGING, LOG, ERR_LOG, TOPIC, newtopic
names = {}
# default server settings
HOST, PORT = "192.168.1.55", 6001
# standart topic
TOPIC = ["WELCOME TO THE NETWORK"]
newtopic = False
#supported commands
supported_commands="supported commands are: help, nick, show, topic, settopic, say"
# switch logging True / False
# default is True
LOGGING = True
# default server logfile
LOG = "server_log.txt"
# default server error logfile
ERR_LOG = "server_err_log.txt"
#local logs
l_log, logtext = [], []
#list of alternative servers
alt_servers=[]

#check recieved message
def unpack_msg(msg):
    if len(msg) == 256:
        splitted=msg.split("#",5)
        if splitted[1] == "dat":
            splitted[5] = splitted[5].rstrip()
            if int(splitted[2]) == (len(splitted[3]) + len(splitted[5])) :
                if int(splitted[4]) == 0:
                    return 0, splitted[3], ""
                else:
                    msg_crc = crc32(splitted[5])
                    if msg_crc == int(splitted[4]):
                        return 0, splitted[3], splitted[5]
                    else: return 1, "", ""
            else: return 1, "", ""
        if splitted[1] == "sys":
            splitted[5] = splitted[5].rstrip()
            if int(splitted[2]) == (len(splitted[3]) + len(splitted[5])):
                if int(splitted[4]) == 0:
                    return 0, splitted[3], ""
                else:
                    msg_crc = crc32(splitted[5])
                    if msg_crc == int(splitted[4]):
                        return 0, splitted[3], splitted[5]
                    else: return 1, "", ""
            else: return 1, "", ""
        else: return 1, "", ""
    else: return 1, "", ""

#packing data for sending
def pack_msg(code, text, type = "#dat#"):
    msg = type+string.zfill(str(len(code)+len(text)),3)+"#"+code+"#"+string.zfill(str(crc32(text)),10)+"#"+text
    return msg.ljust(256)


# the server logging function
# LOGGING function
def logging(text2):
    #doc
    """this is logging function"""
    #open or create a logfile
    logg = open(LOG, "a+")
    #write date and text to the logfile
    logg.write(asctime()+": "+text2+"\n")
    logg.close()
    #add text to temporary log
    if len(l_log) == 20:
        del l_log[0]
    l_log.append(text2)
    if len(logtext) == 20:
        del logtext[0]
    logtext.append(text2)

# the server error logging function
# ERROR LOGGING function
def err_logging(text3):
	# doc
	"""this is the error logging function - log what you want"""
	# open or create a logfile
	err_logg = open(ERR_LOG, "a+")
	# write date and text to the logfile
	err_logg.write(asctime()+": "+str(text3)+"\n")
	# close the logfile
	err_logg.close()


# sending msg to all users
def sendmsg(msg):
    #doc
    """send a message to all users who are online"""
    #a loop to check all users
    for sock in names.keys():
        try:
            sock.send(msg)
        except socket.error, detail:
            err_logging(detail)
            #pack message
            message = pack_msg("ibc", "%s has left the room" % names[sock])
            #logging
            logging("%s has left the room" % names[sock])
            del names[sock]
            sendmsg(message)
            sock.close()
            user = []
            # make a list of all user
            for xuser in names.values():
                user.append(xuser)
            user = dumps(user)
            #pack message
            message = pack_msg("uob", user)
            sendmsg(message)
            break

# SERVER function
class Server:
    # listen for connections 
    class Listen_for_connections(Thread):
        def __init__(self):
            Thread.__init__(self)
            
        # start background job
        def run(self):
            # some globals
            global names, server, read, nums, l_log, newtopic, TOPIC
            try:
                # create server socket
                server = socket.socket(AF_INET, SOCK_STREAM)
                server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
                # bind the socket on a port
                server.bind((HOST, PORT))
                # listen for incoming connections
                server.listen(1)
                # we log when the server starts
            except socket.error, detail:
                err_logging(detail)
            # dictionary of users with socket and nick
            names = {}
            # list of socket filenumbers
            nums = [""]
            print "Server successfully started"
            # the main loop to handle th"Welcome %s" % namee connections
            while 1:
                # EXIT the while loop after server shutdown
                if nums[-1] == "CLOSED":
                    sys.exit(0)
                    break
                # filenumber from the socket (needed for select)
                nums = [server.fileno()]
                # ADD new connections
                for sock in names.keys():
                    nums.append(sock.fileno())
            # select for reading from all sockets
                try:
                    read, write, error = select.select( nums, [], [], 3)
                except select.error, detail:
                    err_logging(detail)
                    break
                # SEARCH in the select list read
                for n in read:
                    # LISTEN for new connections
                    if n == server.fileno():
                        sock, addr = server.accept()
                        # standart user"Welcome %s" % namename is None
                        names[sock] = "None"
                    else:
                        for sock in names.keys():
                            if sock.fileno() == n: break
                        # SET the name
                        if names[sock] == "None":
                            try:
                                rcv_msg = sock.recv(256)
                            except socket.error, detail:
                                err_logging(detail)
                                #pack message
                                message = pack_msg("ibc", "%s has left the room" % names[sock])
                                #logging
                                logging("%s has left the room" % names[sock])
                                del names[sock]
                                sock.close()
                                sendmsg(message)
                                user = []
                                # make a list of all user
                                for xuser in names.values():
                                    user.append(xuser)
                                user = dumps(user)
                                #pack message
                                message = pack_msg("uob", user)
                                sendmsg(message)
                                break
                            #unpack msg
                            err, code, text = unpack_msg(rcv_msg)
                            if err == 1:
                                # kick client
                                message = pack_msg("skm",  "")
                                #logging
                                if LOGGING == True:
                                    logging(name+" was kicked from the room by server")
                                #pack message
                                message = pack_msg("ibc",  "%s was kicked from the room by server"%name)
                                #delete user from list
                                del names[sock]
                                sendmsg(message)
                                #close the socket
                                sock.close()
                                user = []
                                # make a list of all user
                                for xuser in names.values():
                                    user.append(xuser)
                                user = dumps(user)
                                #pack message
                                message = pack_msg("uob", user)
                                sendmsg(message)
                            user1 = []
                            # make a list of all user
                            for zuser in names.values():
                                user1.append(zuser)
                            # set nick
                            name = text
                            # is the nick already in use?
                            if name in user1:
                                #pack message
                                message = pack_msg("psm", "AIU")
                                name="guest"+str(randint(0, 1000))
                            #send logs
                            i=0
                            while i < len(l_log):
                                #pack message
                                message = pack_msg("psm", l_log[i])
                                i += 1
                                try:
                                    sock.send(message)
                                except socket.error,  detail:
                                   err_logging(detail)
                            #send topic
                            #pack message
                            message = pack_msg("tpc", TOPIC[-1])
                            try:
                                sock.send(message)
                            except socket.error,  detail:
                                err_logging(detail)
                                #pack message
                                message = pack_msg("ibc", "%s has left the room" % names[sock])
                                #logging
                                logging("%s has left the room" % names[sock])
                                del names[sock]
                                sock.close()
                                sendmsg(message)
                                user = []
                                # make a list of all user
                                for xuser in names.values():
                                    user.append(xuser)
                                user = dumps(user)
                                #pack message
                                message = pack_msg("uob", user)
                                sendmsg(message)
                                break
                            # set the new nickname
                            names[sock] = name
                            # send messages
                            message = pack_msg("bdc", "Welcome %s" % name)
                            sendmsg(message)
                            user = []
                            # make a list of all user
                            for xuser in names.values():
                                user.append(xuser)
                            user = dumps(user)
                            #pack message
                            message = pack_msg("uob", user)
                            sendmsg(message)
                            #send alt servers addresses
                            #pack message
                            if len(alt_servers) > 0:
                                message = pack_msg("aas", alt_servers[-1])
                                try:
                                    sock.send(message)
                                except socket.error,  detail:
                                    del names[sock]
                                    sock.close()
                                    err_logging(detail)
                                break
                        name = names[sock]
                        # GET a message
                        try:
                            recv_message = sock.recv(256)
                        except socket.error, detail:
                            err_logging(detail)
                            #pack message
                            message = pack_msg("ibc", "%s has left the room" % name)
                            #logging
                            logging("%s has left the room" % names[sock])
                            del names[sock]
                            sendmsg(message)
                            sock.close()
                            user = []
                            # make a list of all user
                            for xuser in names.values():
                                user.append(xuser)
                            user = dumps(user)
                            #pack message
                            message = pack_msg("uob", user)
                            sendmsg(message)
                            break
                        #unpack message
                        err, code, text = unpack_msg(recv_message)
                        if err == 1:
                            # kick client
                            message = pack_msg("skm",  "")
                            #logging
                            if LOGGING == True:
                                logging(name+" has left the room")
                            #pack message
                            message = pack_msg("ibc",  "%s has left the room"%name)
                            #delete user from list
                            del names[sock]
                            sendmsg(message)
                            #close the socket
                            sock.close()
                            user = []
                            # make a list of all user
                            for xuser in names.values():
                                user.append(xuser)
                            user = dumps(user)
                            #pack message
                            message = pack_msg("uob", user)
                            sendmsg(message)
                            break
                        # NICK - change the nickname
                        if code == "nck":
                            user1 = []
                            # make a list of all user
                            for zuser in names.values():
                                user1.append(zuser)
                            # set nick
                            name = text
                            # is the nick already in use?
                            if name in user1:
                                #pack message
                                message = pack_msg("psm", "The nickname is already in use, choose another one.")
                                try:
                                    # already in use
                                    sock.send(message)
                                    break
                                except socket.error, detail:
                                    err_logging(detail)
                                    #pack message
                                    message = pack_msg("ibc", "%s has left the room" % name)
                                    #logging
                                    logging("%s has left the room" % names[sock])
                                    del names[sock]
                                    sock.close()
                                    sendmsg(message)
                                    user = []
                                    # make a list of all user
                                    for xuser in names.values():
                                        user.append(xuser)
                                    user = dumps(user)
                                    #pack message
                                    message = pack_msg("uob", user)
                                    sendmsg(message)
                                    break
                            else:
                                # set the new nickname
                                name2 = names[sock]
                                names[sock] = name
                                #pack message
                                message = pack_msg("bdc", "%s is known as %s" % (name2, name))
                                # send messages
                                sendmsg(message)
                                break
                            break
                        # SHOW all user online
                        if code == "onl":
                            user = []
                            # make a list of all user
                            user_size=0
                            for xuser in names.values():
                                user_size+=len(xuser)
                                user.append(xuser)
                            user2 = "users online: "+str(user)
                            #pack message
                            message = pack_msg("psm", user2)
                            try:
                                #send users list
                                sock.send(message)
                            except socket.error, detail:
                                err_logging(detail)
                                #pack message
                                message = pack_msg("ibc", "%s has left the room" % name)
                                #logging
                                logging("%s has left the room" % names[sock])
                                del names[sock]
                                sock.close()
                                sendmsg(message)
                                user = []
                                # make a list of all user
                                for xuser in names.values():
                                    user.append(xuser)
                                user = dumps(user)
                                #pack message
                                message = pack_msg("uob", user)
                                sendmsg(message)
                            break
                        #set topic
                        if code == "stp":
                            if len(text) > 0:
                                TOPIC.append(text)
                                #pack message
                                message = pack_msg("bdc", str(name)+" set topic to "+str(TOPIC[-1]))
                                sendmsg(message)
                                message = pack_msg("tpc", str(TOPIC[-1]))
                                sendmsg(message)
                                newtopic = True
                                if LOGGING == True:
                                    logging(str(name)+" set topic to "+str(TOPIC[-1]))
                                break
                            else :
                                code == "tpc"
                        #show topic
                        if code == "tpc":
                            #pack message
                            message = pack_msg("psm", TOPIC[-1])
                            try:
                                #send topic
                                sock.send(message)
                            except socket.error, detail:
                                err_logging(detail)
                                #pack message
                                message = pack_msg("ibc", "%s has left the room" % name)
                                #logging
                                logging("%s has left the room" % names[sock])
                                del names[sock]
                                sock.close()
                                sendmsg(message)
                                user = []
                                # make a list of all user
                                for xuser in names.values():
                                    user.append(xuser)
                                user = dumps(user)
                                #pack message
                                message = pack_msg("uob", user)
                                sendmsg(message)
                            break
                        #send a message to all
                        if code == "say":
                            message = pack_msg("bdc", "%s: %s" % (name, text))
                            sendmsg(message)
                            # logging
                            if LOGGING == True:
                                logging(str(name)+": "+text)
                            break
                        #send list of supported commands
                        if code == "hlp":
                            #pack message
                            message = pack_msg("psm", supported_commands)
                            try:
                                #send supported commands
                                sock.send(message)
                            except socket.error, detail:
                                err_logging(detail)
                                #pack message
                                message = pack_msg("ibc", "%s has left the room" % name)
                                #logging
                                logging("%s has left the room" % names[sock])
                                del names[sock]
                                sendmsg(message)
                                sock.close()
                                user = []
                                # make a list of all user
                                for xuser in names.values():
                                    user.append(xuser)
                                user = dumps(user)
                                #pack message
                                message = pack_msg("uob", user)
                                sendmsg(message)
                                user = []
                                # make a list of all user
                                for xuser in names.values():
                                    user.append(xuser)
                                user = dumps(user)
                                #pack message
                                message = pack_msg("uob", user)
                                sendmsg(message)
                            break
                        else:
                            # kick client
                            message = pack_msg("skm",  "")
                            #logging
                            if LOGGING == True:
                                logging(name+" was kicked from the room by server")
                            #pack message
                            message = pack_msg("ibc",  "%s was kicked from the room by server"%name)
                            #delete user from list
                            del names[sock]
                            sendmsg(message)
                            #close the socket
                            sock.close()
                            user = []
                            # make a list of all user
                            for xuser in names.values():
                                user.append(xuser)
                            user = dumps(user)
                            #pack message
                            message = pack_msg("uob", user)
                            sendmsg(message)
    # make instance (needed for threading)
    

# DISCONNECT the server and client connections
def stop():
    # doc
    """close the server with all connections"""
    for sock in names.keys():
        try:
            sock.send("#size "+str(len("server closed the connection")))
        except socket.error, detail:
            err_logging(detail)
        try:
            sock.send("server closed the connection")
        except socket.error, detail:
            err_logging(detail)
        sock.close()
        del names[sock]
    sleep(1)
    #logging
    print "server closed"
    #close the mainserver
    server.close()
    # for a breakout from the threading mainloop
    nums.append("CLOSED")
    
# make a server instance
s=Server()

#START the server
def start():
    #doc
    """start the server"""
    s.lc=s.Listen_for_connections()
    s.lc.setDaemon(True)
    s.lc.start()
#EOF
